package com.echo.im.server.netty.processor.impl;


import com.echo.im.common.enums.IMCmdType;
import com.echo.im.common.model.platform.PlatformCmd;
import com.echo.im.common.model.platform.IMPlatformCmd;
import com.echo.im.mq.event.producer.EventPublisher;
import com.echo.im.mq.event.model.OfflineMessageEvent;
import com.echo.im.server.netty.processor.impl.handler.PlatformCmdHandler;
import com.echo.im.server.netty.processor.AbstractMessageProcessor;
import io.netty.channel.ChannelHandlerContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 1. 浏览器发送 【 `content` `type` `recvId` 】到 `im-platform`
 * 2. `im-platform` 将此消息异步入库
 * 3. `im-platfrom` 请求 `im-client` 进行消息推送
 * 4. `im-client` 通过 `Redis` 查询后获取到接受者连接的 `im-server` 对应的 RabbitMQ routeKey，发送消息到此 routeKey
 *    1. 如果用户不在线，则消息入离线消息队列（如果redis上没有缓存user-serverId键值对，说明用户下线），**至此消息发送成功**，给前端发送 ack（HTTP请求不用管，因为一个HTTP请求已经包含a一来一回了）
 * 5. `im-server` 监听到此消息，进行发送
 *    1. 去 `ConcurrentHashMap` 中取出该用户的 Channel，通过此 Channel 发送消息
 *    2. 如果不存在 `Channel`，则证明用户已经离线，则消息入离线消息队列，**至此消息发送成功**
 *    3. 发送消息，并存入 ack 等待队列，等待接受者的 ack
 *    4. 超时未等到 ack，则重复 **步骤 1** （此处实践上来说失败次数会很少，可忽略不计，没必要过度设计）
 *    5. 收到 ack，则从 ack 等待队列中删除消息，**至此消息发送成功**
 * @author echo
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class PlatformCommandMessageProcessor<T extends PlatformCmd> extends AbstractMessageProcessor<IMPlatformCmd<T>> {

    private final EventPublisher eventPublisher;
    private final OfflineMessageEvent privateOfflineMessageEvent;
    private final List<PlatformCmdHandler<T>> platformCmdHandlerList;

    @Override
    public void process(ChannelHandlerContext ctx, IMPlatformCmd<T> data) {

    }
    /**
     * 处理通过上下文环境接收的平台命令消息
     * 该方法遍历所有支持的平台命令处理器，并调用支持当前命令类型的处理器
     *
     * @param data 接收到的平台命令数据
     */
    @Override
    public void process(IMPlatformCmd<T> data) {
        for (PlatformCmdHandler handler : platformCmdHandlerList) {
            if (handler.support(data.getType())){
                handler.handle(handler.transForm(data.getData()));
            }
        }
    }

    @Override
    public IMCmdType getCmdType() {
        return IMCmdType.PLATFORM_COMMAND_MESSAGE;
    }

}
